﻿Public Class ClassGraphTool
    Structure POINTAPI
        Dim x As Long
        Dim y As Long
    End Structure
    Public Structure RECT
        Dim Left As Long
        Dim Top As Long
        Dim Right As Long
        Dim Bottom As Long
    End Structure

    Structure RGNDATA
        Dim rdh As RGNDATAHEADER
        Dim Buffer As RECT
    End Structure

    Public Structure RGNDATAHEADER
        Dim dwSize As Long
        Dim iType As Long
        Dim nCount As Long
        Dim nRgnSize As Long
        Dim rcBound As RECT
    End Structure

    Public Const RGN_AND = 1
    Public Const RGN_COPY = 5
    Public Const RGN_DIFF = 4
    Public Const RGN_MAX = RGN_COPY
    Public Const RGN_MIN = RGN_AND
    Public Const RGN_OR = 2
    Public Const RGN_XOR = 3
    Public Const ALTERNATE = 1
    Public Const WINDING = 2

    Public Const NULLREGION = 1
    Public Const COMPLEXREGION = 3
    Public Const SIMPLEREGION = 2




    Declare Function CreatePolygonRgn Lib "gdi32" Alias "CreatePolygonRgn" (ByVal lpPoint As POINTAPI, ByVal nCount As Long, ByVal nPolyFillMode As Long) As Long
    Public Declare Function CombineRgn Lib "gdi32" Alias "CombineRgn" (ByVal hDestRgn As Long, ByVal hSrcRgn1 As Long, ByVal hSrcRgn2 As Long, ByVal nCombineMode As Long) As Long
    Public Declare Function CreateRectRgn Lib "gdi32" Alias "CreateRectRgn" (ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long
    Declare Function GetRegionData Lib "gdi32" Alias "GetRegionDataA" (ByVal hRgn As Long, ByVal dwCount As Long, ByRef lpRgnData As RGNDATA) As Long

    ''' <summary>
    ''' ARCGIS实现类
    ''' </summary>
    ''' <remarks></remarks>
    Private Shared arcgistools As ArcgisGraphTool

    Private Function getArcGisTool() As ArcgisGraphTool
        If arcgistools Is Nothing Then
            arcgistools = New ArcgisGraphTool()
        End If
        Return arcgistools
    End Function

    Public Function isInterSectRing(ByRef tmpRing1 As IClassTypeRing, ByRef tmpRing2 As IClassTypeRing) As Integer
        If ClassConsManager.GRAPH_PRIVODE = ClassConsManager.GRAPH_CLASS.GRAPH_PRIVODE_ARCGIS Then
            '使用ARCGSI来进行包含关系的判断
            getArcGisTool()
            isInterSectRing = ArcgisGraphTool.isInterSectRingArcGis(tmpRing1, tmpRing2)
        ElseIf ClassConsManager.GRAPH_PRIVODE = ClassConsManager.GRAPH_CLASS.GRAPH_PRIVODE_WINAPI Then
            '使用windwos API 
            isInterSectRing = isInterSectRingWin32API(tmpRing1, tmpRing2)
        ElseIf ClassConsManager.GRAPH_PRIVODE = ClassConsManager.GRAPH_CLASS.GRAPH_PRIVODE_GEOCODE Then
            '数学判断方式
            isInterSectRing = isInterSectRingGEOCODE(tmpRing1, tmpRing2)
        ElseIf ClassConsManager.GRAPH_PRIVODE = ClassConsManager.GRAPH_CLASS.GRAPH_PRIVODE_NODE_INCLUDE Then
            '节点包含
            isInterSectRing = isInterSectRingNodeInclude(tmpRing1, tmpRing2)
        Else
            '不支持的图形判断方式
        End If
    End Function
    ''' <summary>
    ''' 使用节点包含的方法来判断两个环的关系
    ''' </summary>
    ''' <param name="tmpRing1"></param>
    ''' <param name="tmpRing2"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function isInterSectRingNodeInclude(ByRef tmpRing1 As IClassTypeRing, ByRef tmpRing2 As IClassTypeRing) As Integer
        '使用节点包含的方法来判断两个环的关系
        Dim maxx1, maxy1, minx1, miny1 As Double
        Dim maxx2, maxy2, minx2, miny2 As Double

        If tmpRing1 Is Nothing Then
            isInterSectRingNodeInclude = 0
        End If

        If tmpRing2 Is Nothing Then
            isInterSectRingNodeInclude = 0
        End If

        maxx1 = Integer.MinValue
        maxy1 = Integer.MinValue
        minx1 = Integer.MaxValue
        miny1 = Integer.MaxValue
        For Each tmpNode As TypeNode In tmpRing1.getNodeList
            If tmpNode Is Nothing Then
                Continue For
            End If
            If tmpNode.longitude > maxx1 Then
                maxx1 = tmpNode.longitude
            End If
            If tmpNode.longitude < minx1 Then
                minx1 = tmpNode.longitude
            End If
            If tmpNode.latitude > maxy1 Then
                maxy1 = tmpNode.latitude
            End If
            If tmpNode.latitude < miny1 Then
                miny1 = tmpNode.latitude
            End If
        Next







    End Function
    ''' <summary>
    '''  数学判断方式
    ''' </summary>
    ''' <param name="tmpRing1"></param>
    ''' <param name="tmpRing2"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function isInterSectRingGEOCODE(ByRef tmpRing1 As IClassTypeRing, ByRef tmpRing2 As IClassTypeRing) As Integer
        '判断来环1是否相交于环2，如果是，返回true ，否则返回false
        If tmpRing1.GetRingNo = tmpRing2.GetRingNo Then
            '同一个环，不是包含关系
            isInterSectRingGEOCODE = 0
            Exit Function
        End If

        '  isPointInRing(ByVal n As Long, ByRef ptx() As Double, ByRef pty() As Double, ByVal px As Double, ByVal py As Double) As Boolean
        Dim n As Integer
        n = tmpRing1.getNodeCount
        Dim ptx(n), pty(n) As Double
        Dim i As Integer = 0

        For Each tmpNode As TypeNode In tmpRing1.getNodeList
            ptx(i) = tmpNode.longitude
            pty(i) = tmpNode.latitude
            i = i + 1
        Next
        ptx(i) = tmpRing1.getStartNode.longitude
        pty(i) = tmpRing1.getStartNode.latitude


        Dim gt As ClassGraphTool = New ClassGraphTool
        Dim pointOnRing As Boolean = False
        For Each tmpNode As TypeNode In tmpRing2.getNodeList
            '对环2中的所有节点进行循环判断是否在环1中，如果有一个在，说明环1和环2有相较部分，不是最小环，标记为非最小环，推出

            'For Each tNode As TypeNode In tmpRing1.getNodeList
            '    If tNode.sNodeNo = tmpNode.sNodeNo Then
            '        '比较点是环上一点，不进行比较，继续下一个点
            '        pointOnRing = True
            '        Exit For
            '    End If
            'Next

            If pointOnRing = False Then
                '点不在环上，判断一下点是否在环内
                Dim px, py As Double
                px = tmpNode.longitude
                py = tmpNode.latitude
                Dim isInsect = gt.isPointInRing(n, ptx, pty, px, py)

                If isInsect Then
                    '点在环内
                    tmpRing1.setMinRing(False)
                    isInterSectRingGEOCODE = -1

                    Exit Function
                End If
            End If
        Next
        isInterSectRingGEOCODE = 0
    End Function
    ''' <summary>
    ''' 判断区域是否互相交
    ''' </summary>
    ''' <param name="tmpRing1"></param>
    ''' <param name="tmpRing2"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function isInterSectRingWin32API(ByRef tmpRing1 As IClassTypeRing, ByRef tmpRing2 As IClassTypeRing) As Integer
        '判断区域是否互相交
        Dim points1(), points2() As POINTAPI
        ReDim points1(tmpRing1.getNodeCount), points2(tmpRing2.getNodeCount)
        Dim i As Integer = 0
        For Each tmpNode As TypeNode In tmpRing1.getNodeList
            points1(i).x = tmpNode.longitude
            points1(i).y = tmpNode.latitude
            i = i + 1
        Next
        points1(i).x = tmpRing1.getStartNode.longitude
        points1(i).y = tmpRing1.getStartNode.latitude
        i = 0
        For Each tmpNode As TypeNode In tmpRing2.getNodeList
            points2(i).x = tmpNode.longitude
            points2(i).y = tmpNode.latitude
            i = i + 1
        Next
        points2(i).x = tmpRing2.getStartNode.longitude
        points2(i).y = tmpRing2.getStartNode.latitude

        Dim reg1 = CreatePolygonRgn(points1(0), UBound(points1), ALTERNATE)

        Dim reg2 = CreatePolygonRgn(points2(0), UBound(points2), ALTERNATE)

        Dim result = CreateRectRgn(0, 0, 0, 0)

        Dim nRet = CombineRgn(result, reg1, reg2, RGN_AND)

        If nRet = NULLREGION Then
            '没有互相交叠的区域
            isInterSectRingWin32API = 0
        Else

            '有相交部分，进行比较看谁的大
            Dim size As Long
            Dim retData(1024) As RGNDATA
            Dim r = GetRegionData(result, size, retData(0))



            If tmpRing1.getArea() > tmpRing2.getArea Then
                'tmpRing1 包含tmpring 2
                isInterSectRingWin32API = 1
            Else
                'tmpRing2 包含tmpRing 1
                isInterSectRingWin32API = -1
            End If

        End If

        reg1 = Nothing
        reg2 = Nothing
        result = Nothing

        Debug.Print(tmpRing1.getRingString & "," & tmpRing2.getRingString & "," & Str(nRet))


    End Function


    ''
    '进行判断的工具类
    '判断点px是否在多边形内;
    '即可适用于凹多边形的判断，也适用于凸多边形的判断
    '所选射线px(x0 y0)--pxy(x0+2*max|x0-xi| y0+min|y0-yi|) [i=1,2 3,.......,n]不与多边形任何一顶点相交
    '入口参数多边形：(n, ptx(),  pty(), px , py)
    'n 多边点个数，ptx 多变形点X坐标，pty 多变形点Y坐标，px,py 点的坐标
    '返回值False (在多边形外及边上)、True(在多边形内)
    Public Function isPointInRing(ByVal n As Long, ByRef ptx() As Double, ByRef pty() As Double, ByVal px As Double, ByVal py As Double) As Boolean


        Dim j As Long
        Dim d1 As Double
        Dim d2 As Double
        Dim d3 As Double

        isPointInRing = False

        For i = 1 To n
            j = i + 1 : If i = n Then j = 1
            d1 = Math.Abs(ptx(i) * pty(j) + ptx(j) * py + px * pty(i) - ptx(i) * py - ptx(j) * pty(i) - px * pty(j))
            d2 = Pold(ptx(i), pty(i), ptx(j), pty(j))
            d3 = Math.Abs(d2 - Pold(ptx(i), pty(i), px, py) - Pold(ptx(j), pty(j), px, py))
            d1 = d1 / d2
            'Print "i=" + Str(i) + " j=" + Str(j) + "  d1=" + Str(d1) + " d3=" + Str(d3)
            '注意：d1 d3判断值1前的0个数=多边形区域坐标值中小数位数-1
            If d1 < 0.0000001 And d3 < 0.0000001 Then isPointInRing = True : Exit Function
        Next i

        If isPointInRing = False Then
            Dim dx As Double
            Dim xmax As Double
            Dim dy As Double
            Dim ymin As Double

            For i = 1 To n
                dx = Math.Abs(ptx(i) - px) : dy = Math.Abs(pty(i) - py)
                If i = 1 Then
                    xmax = dx : ymin = 1.0E+99
                Else
                    If dx > xmax Then xmax = dx
                    If dy < ymin And dy <> 0.0 Then ymin = dy
                End If
            Next i

            Dim sum As Long

            sum = 0 : xmax = 2.0# * xmax
            For i = 1 To n
                j = i + 1 : If i = n Then j = 1
                d1 = ymin * (ptx(j) - ptx(i)) - xmax * (pty(j) - pty(i))
                d2 = xmax * (pty(i) - py) - ymin * (ptx(i) - px)
                d3 = (ptx(j) - ptx(i)) * (pty(i) - py) - (pty(j) - pty(i)) * (ptx(i) - px)
                If (d2 * (d1 - d2)) >= 0.0# And d3 * d1 >= 0.0# Then sum = sum + 1
            Next i
            'Print "sum=" + Str(sum) + " px=" + Str(px) + " py=" + Str(py)
            If sum > 0 And sum <> 2 * Int(sum / 2) Then
                isPointInRing = True
            Else
                isPointInRing = False
            End If
        End If
    End Function
    Function Pold(ByVal x1 As Double, ByVal y1 As Double, ByVal x2 As Double, ByVal y2 As Double) As Double
        '两点间距离计算
        Pold = Math.Sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1))
    End Function





End Class
